<?php
declare(strict_types=1);

namespace App\Service\Web;

use App\Cache\CacheKey;
use App\Enum\Code;
use App\Event\Web\ArticlePublishEvent;
use App\Exception\BusinessException;
use App\Model\Article;
use App\Repository\Web\ArticleRepository;
use Carbon\Carbon;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\Redis;

class ArticleService
{
    #[Inject]
    private ArticleRepository $articleRepository;

    #[Inject]
    private Redis $redis;

    public function publish(int $id, array $data)
    {
        $article = $this->articleRepository->find($id);

        switch ($data['strategy']) {
            case 'immediately':
                $article->status = 'published';
                $article->published_at = Carbon::now();
                break;
            case 'scheduled':
                event(new ArticlePublishEvent($id, $data['published_at']));
                break;
            case 'self':
                $article->status = 'archived';
                $article->published_at = null;
                break;
            case 'password':
                $article->status = 'published';
                $article->published_at = Carbon::now();
                $article->password = $data['password'];
                break;
        }

        return $this->articleRepository->save($article);
    }

    public function setStatus(int $id, string $status)
    {
        $article = $this->articleRepository->find($id);
        $article->status = $status;

        if ($status === 'published') {
            $article->published_at = Carbon::now();
        }

        return $this->articleRepository->save($article);
    }

    public function store(array $data, int $userId)
    {
        return $this->articleRepository->create($data, $userId);
    }

    public function commitToReview(int $id, int $userId)
    {
        $article = $this->articleRepository->findUserDraft($id, $userId);

        if (!$article) {
            throw new BusinessException(Code::ArticleNotFound, '文章未找到');
        }

        $article->status = 'review';
        return $this->articleRepository->save($article);
    }

    public function getPage(array $params, ?int $authUserId, int $page, int $perPage)
    {
        $order = $params['order'] ?? 'id';
        $direction = $params['direction'] ?? 'desc';

        $allowedOrders = ['id', 'created_at', 'updated_at', 'published_at'];
        if (!in_array($order, $allowedOrders)) {
            $order = 'id';
        }

        $articles = $this->articleRepository->getPage($params, $order, $direction, $authUserId, $page, $perPage);

        return map_paginator($articles, function (Article $article) {});
    }

    public function show(int $id, ?int $userId, $ip)
    {
        $article = $this->articleRepository->find($id);

        $key = CacheKey::userViewArticle($id, Carbon::now());

        if ($userId) {
            $this->redis->sAdd($key, $userId);
        } elseif ($ip) {
            $this->redis->sAdd($key, $ip);
        }

        if ($this->redis->exists($key)) {
            $this->redis->expire($key, Carbon::tomorrow()->diffInSeconds(Carbon::now()));
            $this->articleRepository->incrementViewCount($article);
        }

        return $article;
    }
}